﻿// Inner Fire 游戏引擎库
// AnimationBlend - 混合动画
//
// Copyright (c) 2025 by 尔西大帝. All rights reserved.
//
// 所属层级：应用层(4)
// 用于保存项目的混合动画
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2025-04-30

#pragma once

#include <game/animation/animation_clip.h>
#include <game/resource/resource.h>
#include <ref_info.h>
#include <static_string.h>
#include <xml_reader.h>

namespace ifire::game {

struct AnimationBlendItem {
  // 此项目的值，在[0,1]之间。
  float value;
  // 对于Clip的引用
  ikit::rinfo<AnimationClip> clip;

  AnimationBlendItem(const ikit::XmlNode& node);
  const AnimationClip* GetClip() const;
  void Setup();
  // 使这个项目的东西载入
  void Ready() const;
};

/// <summary>
/// 用于返回单个Item中计算结果。
/// </summary>
struct BlendItemWeight {
  // 这个Clip所占的权重
  float weight;
  // 项目的Clip
  AnimationClip* clip;
};

/// <summary>
/// BlendTree的设计思路。在Unity中，拥有五种不同的项目，这导致在数学计算上非常复杂。
///   我使用了极坐标系统来设计项目，通过Line和Angle来设计，进行统一的管理。而将X和Y化为极坐标，来定位Blend。
/// </summary>
struct AnimationBlendRay {
  // 此射线在极坐标中的角度(theta)。如果只有一根线，则设置为0度。
  // 如果是四方向，就设置为θ=0∘,90∘,180∘,270∘
  // 如果是八方向，θ=0∘,45∘,90∘,135∘,180∘,225∘,270∘,315∘
  float direction;
  // 此射线方向的项目
  eastl::vector<AnimationBlendItem> items;

  AnimationBlendRay(const ikit::XmlNode& node);

  // 通过权重获得对象
  void Calculate(float weight_value, BlendItemWeight& a, BlendItemWeight& b);

  // 仅设置一个项目（快捷方式）
private:
  void SetOnly(
      AnimationBlendItem& item, BlendItemWeight& a, BlendItemWeight& b);
};

/// <summary>
/// 用于返回Ray计算结果。
/// </summary>
struct BlendRayWeight {
  // 这个Ray所占的权重
  float weight;
  // 项目的Ray
  AnimationBlendRay* ray;
};

class AnimationBlend : public Resource {
public:
  AnimationBlend(Assets* manager, const ikit::XmlNode& node);
  AnimationBlend(const AnimationBlend& lhs) = delete;
  AnimationBlend& operator=(const AnimationBlend& lhs) = delete;
  ~AnimationBlend() = default;

  // 使这个Blend载入（遍历CLIP，使每个CLIP载入）
  void Ready();
  // 配置这个Blend，使引用生效
  void Setup() override;

  // 是否是Linear，如果只有一个项目，则表示是Linear
  bool IsLinear() const { return rays_.size() == 1; }
  // 是否是Humanoid动画
  bool IsHumanoid() const { return is_humanoid; }
  // 是否使用RootMotion
  bool IsRootMotion() const { return is_root_motion; }

  // 按索引得到Ray
  AnimationBlendRay& GetRay(int index);

private:
  // 用于保存自已的CLIP
  eastl::vector<AnimationBlendRay> rays_;
  // 是否是Humanoid动画
  bool is_humanoid;
  // 是否使用RootMotion
  bool is_root_motion;
};

using AnimationBlendPtr = std::unique_ptr<AnimationBlend>;

} // namespace ifire::game